package top.wshape1.easyscan.hardware;

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import java.util.Arrays;
import java.util.Collections;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2023-05-16
 */
public class MyCamera {
    public static final String TAG = "MyCamera";

    private final MainActivity mainActivity;

    private final TextureView mTextureView;

    // camera
    private final CameraManager mCameraManager;
    private CameraDevice mCameraDevice;
    private CaptureRequest.Builder mCaptureRequestBuilder;

    private CameraCaptureSession mCameraCaptureSession;
    private String mCameraId = "0";
    private CameraCharacteristics characteristics;
    private boolean flashAvailable = false;
    private Size previewSize;


    public MyCamera(MainActivity mainActivity, CameraManager cameraManager, TextureView textureView) {
        this.mainActivity = mainActivity;
        this.mCameraManager = cameraManager;
        this.mTextureView = textureView;

        this.mCameraId = getCameraId();

        previewSize = new Size(mTextureView.getWidth(), mTextureView.getHeight());
        Log.i(TAG, "previewSize ***" + previewSize);
    }

    public CaptureRequest.Builder getCaptureRequestBuilder() {
        return mCaptureRequestBuilder;
    }

    //////////////////

    /**
     * 获取第一个后置摄像头的id (我觉得第一个一般都是主摄)
     *
     * @return 0 or 1 (maybe)
     */
    private String getCameraId() {
        // 获取后置摄像头id
        String retCameraId = "0";

        try {
            for (String cameraId : mCameraManager.getCameraIdList()) {

                characteristics = mCameraManager.getCameraCharacteristics(cameraId);
                if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK) {
                    Log.i(TAG, "back_cameraId: " + cameraId);
                    retCameraId = cameraId;
                    flashAvailable = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
//             打印 硬件与api2的契合度，0-4 越高越契合
                    Log.i(TAG, "deviceLevel = " + characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL));
                    break;
                }
            }
        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }
        return retCameraId;
    }


    /**
     * 根据 mTextureView 比例选择一个最大且比例最相近的相机输出尺寸
     *
     * @param sizes
     * @param previewViewWidth
     * @param previewViewHeight
     * @return
     */
    private Size getOptimalPreviewSize(Size[] sizes, int previewViewWidth, int previewViewHeight) {

        double previewRate = (double) previewViewHeight / previewViewWidth;
        int bestIndex = 0;
        double bestRateDiff = 10000000;

        Log.i(TAG, "previewRate " + previewRate);

        for (int i = 0; i < sizes.length; i++) {
            double rate = (double) sizes[i].getHeight() / sizes[i].getWidth();
            double rateDiff = Math.abs(previewRate - rate);
            if (rateDiff < bestRateDiff) {
                bestIndex = i;
                bestRateDiff = rateDiff;
            }
            Log.i(TAG, i + ": " + sizes[i] + " Rate " + rate);
        }
        return sizes[bestIndex];
    }

    /**
     * 设置previewSize 并 调用getOptimalPreviewSize
     */
    private void setPreviewSize() {
        previewSize = new Size(mTextureView.getHeight(), mTextureView.getWidth());

        StreamConfigurationMap streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

        Size[] outputSizes = streamConfigurationMap.getOutputSizes(SurfaceTexture.class);

        previewSize = getOptimalPreviewSize(outputSizes, previewSize.getWidth(), previewSize.getHeight());

        Log.i(TAG, "Has HxW " + Arrays.toString(outputSizes));
        Log.i(TAG, "Select " + previewSize);
    }

    /**
     * 创建CameraPreviewSession
     */
    private void createCameraPreviewSession() {
        SurfaceTexture texture = mTextureView.getSurfaceTexture();

        setPreviewSize();
        Log.i(TAG, "createCameraPreviewSession Size: " + new Size(mTextureView.getHeight(), mTextureView.getWidth()));


        texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());

        Surface surface = new Surface(texture);
        try {
            mCameraDevice.createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    try {
                        mCameraCaptureSession = session;
                        mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                        mCaptureRequestBuilder.addTarget(surface); // 必须设置才能正常预览
                        // 设置持续自动对焦
                        mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                        CaptureRequest captureRequest = mCaptureRequestBuilder.build();
                        // CameraCaptureSession与CaptureRequest绑定（这是最后一步，已可显示相机预览）
                        session.setRepeatingRequest(captureRequest, mCaptureCallback, null);
                    } catch (CameraAccessException e) {
                        Log.e(TAG, "createCaptureRequest error = " + e.getMessage());
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                }

            }, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } finally {
            surface.release();
        }
    }

    /**
     * 开关闪关灯
     *
     * @param ifOpen
     */
    public boolean toggleFlash(boolean ifOpen) {
        if (!flashAvailable) {
            Toast.makeText(mainActivity, "闪关灯打开失败！", Toast.LENGTH_LONG).show();
            return false;
        }
        try {
            mCaptureRequestBuilder.set(CaptureRequest.FLASH_MODE, ifOpen ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
            CaptureRequest captureRequest = mCaptureRequestBuilder.build();
            mCameraCaptureSession.setRepeatingRequest(captureRequest, mCaptureCallback, null);
            return true;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 打开摄像头
     */
    public void openCamera() {
        if (ActivityCompat.checkSelfPermission(mainActivity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            try {
                Log.i(TAG, "Begin open Camera");
                mCameraManager.openCamera(mCameraId, mStateCallback, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
//        else {
//            Toast.makeText(mainActivity, "无法获取摄像头权限， 请手动授权！", Toast.LENGTH_LONG).show();
//            mainActivity.finish();
//        }
    }

    /**
     * 关闭摄像头
     */
    public void closeCamera() {
        if (mCameraDevice != null) {
            mCameraDevice.close();
        }
    }


    ////////////////////////////////////////////////////


    /**
     * cameraManager.openCamera 回调
     */
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            mCameraDevice = camera;
            createCameraPreviewSession();
            Log.i(TAG, "Camera Opened!");
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            closeCamera();
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            closeCamera();
        }
    };


    /**
     * createCameraCaptureSession 回调
     */
    private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
    };


}
